/**
 *	@brief	Main testing file
 *	@file	PluglibTest.cpp contains definitions of tests
 *	@author Szymon Janikowski
 *	@author Pawel Gilewski
 */

///@brief Debug flag
#define DEBUG_F 1

#include <boost/filesystem.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/shared_ptr.hpp>

#include "../Pluglib/Engine.hpp"
#include "FirstInterface.hpp"
#include "SecondInterface.hpp"
#include "FirstInterfaceFactory.hpp"
#include "SecondInterfaceFactory.hpp"
//#include  "../faif/utils/actobj/Command.h"
//#include  "../faif/utils/actobj/Scheduler.h"


#include <iostream>
#include <string>
#ifdef WIN32
#include <windows.h>
#else
#include <time.h>
#endif


using namespace boost::filesystem;
using namespace boost::unit_test;
using namespace std;
//using namespace faif;

/*class AddPluginCommand : public Command
{
	AddPluginCommand(string filename, const Engine& engine)
	{
		filename_ = filename;
		engine_ = engine;
	}
	virtual void operator()(Progress& progress)
	{
		engine_.loadPlugin(filename_)
	}
private:
	string filename_;
	Engine& engine_;
}

template<typename ResultType>
class ActionRepeater : public actobj::Command
{
public:
	ActionRepeater( boost::function<ResultType ()> action, ResultType& result, int repeats,int delay)
	{
		action_ = action;
		result_ = result;
		repeats_ = repeats ;
		delay_ =delay;
	}

	virtual void operator()(actobj::Progress& progress)
	{
		for(int i=0;i<repeats_;i++)
		{
			result_=action_();
			progress.setProgress(i/repeats_);
			#ifdef DEBUG_F
			cout << "Iteration: " << i;
			#endif

			/*#ifdef WIN32
			Sleep(delay_*1000);
			#else
			sleep(delay_);
			#endif*/
/*			boost::this_thread::sleep(boost::posix_time::milliseconds(delay_));
			
		}
	}

private:
	//typename boost::function<void> ActionType 
	boost::function<ResultType ()>  action_;
	int repeats_;
	int delay_;
	ResultType result_;
};
*/


/// @brief Loads plugins from a given direcotry
/// @param engine reference to pass to external function
/// @param filePath path to dynamic libraries
void loadPlugins(Engine& engine, string filePath)
{
	

	//path pwd = path (".");
	//cout << pwd.root_directory();
	path pluginPath(filePath); pluginPath.remove_leaf();
	directory_iterator end_itr;
	for ( directory_iterator itr( pluginPath );itr != end_itr;++itr )
	{
		#ifdef DEBUG_F
		cout << itr->path() << endl;
		#endif
		#ifdef _WIN32
		if (extension(itr->path().leaf()) == ".dll")
		#else
		if (extension(itr->path().leaf()) == ".so")
		#endif
		{
			cout << "Trying to load " << pluginPath.native_directory_string() + itr->filename() << endl;
			#ifdef _WIN32
			if (engine.loadPlugin(pluginPath.native_directory_string() + itr->filename()))
				cout << "Success!" << endl;
			else
				cout << "Failure" << endl;
			#else
			if (engine.loadPlugin(pluginPath.native_directory_string() + "/" +  itr->filename()))
				cout << "Success!" << endl;
			else
				cout << "Failure" << endl;
			#endif
		}
	}
};


///@brief Various test cases using single thread
void singleThreadTest ()
{

	using namespace pluglib;

	Engine &engine = Engine::getInstance();

	boost::shared_ptr<FactoriesServer<FirstInterface> > firstInterfaceServer(new FactoriesServer<FirstInterface>(FirstInterfaceFactory::serverName_));
	Engine::getInstance().addServer(firstInterfaceServer->getName(),firstInterfaceServer.get());

	boost::shared_ptr<FactoriesServer<SecondInterface> > secondInterfaceServer(new FactoriesServer<SecondInterface>(SecondInterfaceFactory::serverName_));
	Engine::getInstance().addServer(secondInterfaceServer->getName(),secondInterfaceServer.get());

	#ifdef _WIN32
	  loadPlugins(engine, "..\\debug\\PluglibPlugin.dll");	
	#else
		loadPlugins(engine, ".");
	#endif
	

	vector<string> interfaces;
	vector<string> firstImplementations;
	vector<string> secondImplementations;
	//Engine::getInstance().getInterfaceNames(interfaces);
	//FactoriesServerBase server = Engine::getServer(interfaces[0])
	firstInterfaceServer->getFactoriesNames(firstImplementations);
	secondInterfaceServer->getFactoriesNames(secondImplementations);

	BOOST_REQUIRE(firstImplementations.size() > 0);
	BOOST_REQUIRE(secondImplementations.size() > 0);

	boost::shared_ptr<FirstInterface> firstInterfaceObject(firstInterfaceServer->create(firstImplementations[0]));
	firstInterfaceObject->action();
	firstInterfaceObject->action("param action text");
	boost::shared_ptr<FirstInterface> firstInterfaceObject2(firstInterfaceServer->create("SecondImplOfFirst"));
	firstInterfaceObject2->action();
	firstInterfaceObject2->action("param action text");
	
	boost::shared_ptr<SecondInterface> secondInterfaceObject(secondInterfaceServer->create(secondImplementations[0]));
	secondInterfaceObject->action();
	//delete firstInterfaceObject;
	//std::cin.get();
};

/*void multiThreadTest()
{
	using namespace pluglib;

	Engine &engine = Engine::getInstance();
	actobj::Scheduler &scheduler = actobj::Scheduler::getInstance();

	FactoriesServer<FirstInterface>* firstInterfaceServer = new FactoriesServer<FirstInterface>(FirstInterfaceFactory::serverName_);
	Engine::getInstance().addServer(firstInterfaceServer->getName(),firstInterfaceServer);

	//cout << "Testing application for Pluglib" << endl;

	loadPlugins(engine);

	vector<string> interfaces;
	vector<string> implementations;

	//Engine::getInstance().getInterfaceNames(interfaces);
	//FactoriesServerBase server = Engine::getServer(interfaces[0])
	firstInterfaceServer->getFactoriesNames(implementations);
	FirstInterface* firstInterfaceObject1 ;//=firstInterfaceServer->create(implementations[0]);
	FirstInterface* firstInterfaceObject2 ;//=firstInterfaceServer->create(implementations[1]);

	boost::function<FirstInterface*()> createPointer1 = boost::bind(&(FactoriesServer<FirstInterface>::create),
		firstInterfaceServer,
		implementations[0]);
	boost::function<FirstInterface*()> createPointer2 = boost::bind(&(FactoriesServer<FirstInterface>::create),firstInterfaceServer,implementations[1]);

	actobj::PCommand repeatActionfromFirstImplOfFirst = 
		new ActionRepeater<FirstInterface*>(createPointer1,firstInterfaceObject1,10,1);
	actobj::PCommand repeatActionfromSecondImplOfFirst = 
		new ActionRepeater<FirstInterface*>(createPointer2,firstInterfaceObject2,10,1);

	scheduler.executeAsynchronously(repeatActionfromFirstImplOfFirst);
	scheduler.executeAsynchronously(repeatActionfromSecondImplOfFirst);

	firstInterfaceObject1->action();
	firstInterfaceObject2->action();

	std::cin.get();
}
*/


/// @brief boost::test test_suite function - instead of main
test_suite*
init_unit_test_suite( int, char* [] ) {
	test_suite* test= BOOST_TEST_SUITE( "Pluglib test suite" );
	test->add(BOOST_TEST_CASE(&singleThreadTest));
    test->add(BOOST_TEST_CASE(&multiThreadTest));
	return test;
}